package ru.gelin.android.bells;

import static ru.gelin.android.bells.TimerDbOpenHelper.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import ru.gelin.android.bells.timers.Timer;
import ru.gelin.android.bells.timers.TimerCollection;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class TimerStorage extends TimerCollection implements Constants {
    
    /** Name of the preferences */
    static final String PREFERENCES_NAME = "TimerStorage";
    
    /** "enabled" property suffix */
    static final String ENABLED_SUFFIX = "_enabled";
    /** "start" property suffix */
    static final String START_SUFFIX = "_start";
    /** "period hours" property suffix */
    static final String PERIOD_HOURS_SUFFIX = "_period_hours";
    /** "period minutes" property suffix */
    static final String PERIOD_MINUTES_SUFFIX = "_period_minutes";
    /** "message" property suffix */
    static final String MESSAGE_SUFFIX = "_message";
    /** "alarm" property suffix */
    static final String ALARM_SUFFIX = "_alarm";

    /** Storage instance */
    static TimerStorage instance;
    
    /** Context which uses this storage */
    Context context;
    /** Preferences which saves the timers */
    SharedPreferences preferences;
    /** Database open helper */
    SQLiteOpenHelper dbHelper;

    /**
     *  Private constructor.
     */
    private TimerStorage(Context context) {
        this.context = context;
        preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
        dbHelper = new TimerDbOpenHelper(context);
    }
    
    public static TimerStorage getInstance(Context context) {
        if (instance == null) {
            instance = new TimerStorage(context);
        }
        return instance;
    }
    
    public void load() {
        delete();
        
        loadFromPreferences();      //backward compatibility
        SharedPreferences.Editor editor = preferences.edit();
        editor.clear();
        editor.commit();
        
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = db.query(TIMER_TABLE, 
                new String[] {ENABLED, START, PERIOD_HOURS, PERIOD_MINUTES, MESSAGE, ALARM},
                null, null, null, null, null);
        int enabledIndex = cursor.getColumnIndex(ENABLED);
        int startIndex = cursor.getColumnIndex(START);
        int periodHoursIndex = cursor.getColumnIndex(PERIOD_HOURS);
        int periodMinutesIndex = cursor.getColumnIndex(PERIOD_MINUTES);
        int messageIndex = cursor.getColumnIndex(MESSAGE);
        int alarmIndex = cursor.getColumnIndex(ALARM);
        if (cursor.moveToFirst()) {
            do {
                Timer timer = new Timer();
                timer.setEnabled(cursor.getInt(enabledIndex) != 0);
                timer.setStartTime(new Date(cursor.getLong(startIndex)));
                timer.setPeriod(cursor.getInt(periodHoursIndex),
                        cursor.getInt(periodMinutesIndex));
                timer.setMessage(cursor.getString(messageIndex));
                timer.setAlarm(cursor.getString(alarmIndex));
                insert(timer);
            } while (cursor.moveToNext());
        }
        cursor.close();
        db.close();
    }
    

    
    /**
     *  Saves timers to the persistent storage.
     */
    public void save() {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.beginTransaction();
        try {
            db.delete(TIMER_TABLE, null, null);
            for (Timer timer : internalList()) {
                ContentValues values = new ContentValues();
                values.put(ID, timer.getId());
                values.put(ENABLED, timer.isEnabled());
                values.put(START, timer.getStartTime().getTime());
                values.put(PERIOD_HOURS, timer.getPeriod().getHours());
                values.put(PERIOD_MINUTES, timer.getPeriod().getMinutes());
                values.put(MESSAGE, timer.getMessage());
                values.put(ALARM, timer.getAlarm());
                db.insert(TIMER_TABLE, null, values);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
        db.close();
    }
    
    /**
     *  Schedulers next alarm using AlarmManager.
     *  On alarm AlarmActivity will start.
     *  @param  now current time
     */
    public void scheduleAlarm(Date now) {
        Intent intent = new Intent(context, AlarmActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent =
            PendingIntent.getActivity(context, 0, intent, 0);
        
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(
                Context.ALARM_SERVICE);
        Date nextAlarm = alarmTime(now);
        
        if (nextAlarm == null) {
            Log.i(TAG, "cancelling alarm scheduler");
            alarmManager.cancel(pendingIntent);
            return;
        }
        
        Log.i(TAG, "scheduling alarm on " + nextAlarm);
        alarmManager.set(AlarmManager.RTC_WAKEUP, 
                nextAlarm.getTime(), pendingIntent);
    }
    
    /**
     *  Schedulers next alarm relative the the current time.
     */
    public void scheduleAlarm() {
        scheduleAlarm(new Date());
    }
    
    /**
     *  Loads timers from the persistent storage.
     */
    void loadFromPreferences() {
        Map<String, ?> values = preferences.getAll();
        Map<Integer, Timer> result = new HashMap<Integer, Timer>();
        for (Map.Entry<String, ?> entry : values.entrySet()) {
            String key = entry.getKey();
            int id;
            try {
                id = Integer.parseInt(key.substring(0, key.indexOf('_')));
            } catch (Exception e) {
                Log.w(TAG, "invalid preferences key: " + key);
                continue;
            }
            Timer timer = result.get(id);
            if (timer == null) {
                timer = new Timer();
                result.put(id, timer);
            }
            try {
                if (key.endsWith(ENABLED_SUFFIX)) {
                    timer.setEnabled((Boolean)entry.getValue());
                } else if (key.endsWith(START_SUFFIX)) {
                    timer.setStartTime(new Date((Long)entry.getValue()));
                } else if (key.endsWith(PERIOD_HOURS_SUFFIX)) {
                    timer.setPeriod((Integer)entry.getValue(), timer.getPeriod().getMinutes());
                } else if (key.endsWith(PERIOD_MINUTES_SUFFIX)) {
                    timer.setPeriod(timer.getPeriod().getHours(), (Integer)entry.getValue());
                } else if (key.endsWith(MESSAGE_SUFFIX)) {
                    timer.setMessage((String)entry.getValue());
                } else if (key.endsWith(ALARM_SUFFIX)) {
                    timer.setAlarm((String)entry.getValue());
                }
            } catch (Exception e) {
                Log.w(TAG, "invalid preferences value: " + key + " = " + entry.getValue());
                result.remove(id);
            }
        }
        //delete();
        for (Timer timer : result.values()) {
            insert(timer);
        }
    }

}
